#include <EEPROM.h>
#define BLINKER_WIFI
#define BLINKER_APCONFIG
#define BLINKER_MIOT_LIGHT
#include <Blinker.h>
#include <WS2812FX.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
char auth[] = "2898e265fc6c";//yx
//char auth[] = "cf438260ecd1";//yx2

//char auth[] = "338e58cf4dd1";//kmy
//char auth[] = "0d4cc3e546e8";//xj
#include "OneButton.h"
#define BLINKER_BUTTON_PIN D3
//char ssid[] = "小公路";
//char pswd[] = "KeHome604";


#define readHist       //上电后会读取上次存储的模式-亮度-颜色-速度，如果不需要请注释掉！
#define LED_PIN D8     //ESP8266-Node MCU开发板 D8引脚
#define LED_COUNT 60  //灯珠数量

int Mode = 0;          //初始模式  温馨提示：固定颜色为模式0
int Speed = 100;       //初始速度  由于存储设置限制，实际时间为10倍数 毫秒
uint8_t colorW = 127;  //初始亮度
uint8_t colorR = 139;  //初始颜色RGB
uint8_t colorG = 0;
uint8_t colorB = 250;

bool wsState;
uint8_t wsMode = BLINKER_CMD_MIOT_DAY;  //语音助手模式定义
OneButton button(BLINKER_BUTTON_PIN, true);
const char* host = "esp8266-webupdate";

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

BlinkerText IPaddr("IPaddress");
BlinkerText Text2("RSSI");
BlinkerText Text3("runTime");
BlinkerButton Button_on("btn-1");  //app中按键的ID
BlinkerButton Button_Save("saveSetting");
BlinkerButton Button_Read("readSetting");
BlinkerText modeSaved("savedMode");
BlinkerRGB WS2812("RGB");
BlinkerSlider Slider_ColorTemperature("ColorTemperature");  //色温
BlinkerSlider Slider_Mode("Mode");
BlinkerSlider Slider_Speed("Speed");
BlinkerText modeName("ModeName");


ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";
 
void deviceReset() {
  // Reset device ,erase WiFi config.
  Blinker.reset();
}

// 按下按键即会执行该函数
void on_callback(const String& state) {
  BLINKER_LOG("get button state: ", state);
  if (state == "on") {
    ws2812fx.setMode(Mode);
    ws2812fx.setColor(colorR, colorG, colorB);
    colorW = 127;
    ws2812fx.setBrightness(colorW);
    WS2812.brightness(colorW);
    digitalWrite(LED_BUILTIN, LOW);
    Button_on.print("on");
  } else if (state == "off") {
    ws2812fx.setMode(Mode);
    colorW = 0;
    ws2812fx.setBrightness(colorW);


    digitalWrite(LED_BUILTIN, HIGH);
    Button_on.print("off");
  }
  Blinker.vibrate();

  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");            //WIFI信号强度
  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");
  WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
  Slider_Mode.print(Mode);                       //滑动条
  Slider_Speed.print(Speed);
  modeName.print(ws2812fx.getModeName(Mode));  //文字
}

void save_callback(const String& state) {

  BLINKER_LOG("get button state: ", state);

  if (state == BLINKER_CMD_BUTTON_TAP) {
    EEPROM.begin(4096);
    BLINKER_LOG("存储!");
    EEPROM.write(2500, int(colorR));
    delay(50);
    EEPROM.write(2501, int(colorG));
    delay(50);
    EEPROM.write(2502, int(colorB));
    delay(50);
    EEPROM.write(2503, int(colorW));
    delay(50);
    EEPROM.write(2504, int(Speed));
    delay(50);
    EEPROM.write(2505, int(Mode));
    delay(50);
    EEPROM.commit();
    if (EEPROM.commit()) {
      Serial.println("模式存储成功！");
      modeSaved.print("已存储模式", String(ws2812fx.getModeName(Mode)));
      Blinker.vibrate();
    } else {
      Serial.println("模式存储失败！");
    }
  }
}

void read_callback(const String& state) {

  BLINKER_LOG("get button state: ", state);

  if (state == BLINKER_CMD_BUTTON_TAP) {
    EEPROM.begin(4096);
    BLINKER_LOG("读取!");

    colorR = EEPROM.read(2500);
    colorG = EEPROM.read(2501);
    colorB = EEPROM.read(2502);
    colorW = EEPROM.read(2503);
    Speed = EEPROM.read(2504);
    Mode = EEPROM.read(2505);
    Serial.println(colorR);
    Serial.println(colorG);
    Serial.println(colorB);
    Serial.println(colorW);
    Serial.println(Speed);
    Serial.println(Mode);
    Blinker.vibrate();
    ws2812fx.setMode(Mode);
    ws2812fx.setSpeed(Speed * 100);
    ws2812fx.setColor(colorR, colorG, colorB);
    ws2812fx.setBrightness(colorW);
    Button_on.print("on");

    WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
    Slider_Mode.print(Mode);                       //滑动条
    Slider_Speed.print(Speed);
    modeName.print(ws2812fx.getModeName(Mode));  //文字
  }
}
void ws2812_callback(uint8_t r_value, uint8_t g_value, uint8_t b_value, uint8_t bright_value) {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  BLINKER_LOG("R value: ", r_value);
  BLINKER_LOG("G value: ", g_value);
  BLINKER_LOG("B value: ", b_value);
  BLINKER_LOG("Rrightness value: ", bright_value);

  colorR = r_value;
  colorG = g_value;
  colorB = b_value;
  colorW = bright_value;

  ws2812fx.setBrightness(colorW);
  ws2812fx.setColor(colorR, colorG, colorB);
  if (colorW > 0) {
    Button_on.print("on");
  } else {
    Button_on.print("off");
  }

  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");  //WIFI信号强度

  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");
}

//APP 色温滑动条设置回调
void Slider_ColorTemperature_callback(int32_t value) {

  BLINKER_LOG("get slider value: ", value);
  Color_temperature_conversion(value, &colorR, &colorG, &colorB);  //色温转RGB
  ws2812fx.setMode(0);
  ws2812fx.setColor(colorR, colorG, colorB);
  WS2812.brightness(colorW);
  WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息

  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");            //WIFI信号强度
  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");
}

void Mode_callback(int32_t value) {

  BLINKER_LOG("模式切换: ", value);
  Mode = int(value);
  ws2812fx.setMode(Mode);
  Serial.println(ws2812fx.getModeName(Mode));
  modeName.print("当前模式", String(ws2812fx.getModeName(Mode)));

  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");            //WIFI信号强度
  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");
}

void Speed_callback(int32_t value) {

  BLINKER_LOG("速度调整: ", value);
  Speed = int(value);
  ws2812fx.setSpeed(Speed * 100);
  // Slider_Speed.print(Speed);

  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");            //WIFI信号强度
  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");
}


uint32_t getColor() {
  uint32_t color = colorR << 16 | colorG << 8 | colorB;

  return color;
}

void miotPowerState(const String& state) {
  BLINKER_LOG("need set power state: ", state);

  if (state == BLINKER_CMD_ON) {
    digitalWrite(LED_BUILTIN, LOW);

    BlinkerMIOT.powerState("on");
    BlinkerMIOT.print();

    wsState = true;


    if (colorW == 0) {
      colorW = 127;
    }
    ws2812fx.setBrightness(colorW);
    // 反馈开关状态
    Button_on.print("on");
    WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息

  } else if (state == BLINKER_CMD_OFF) {
    digitalWrite(LED_BUILTIN, HIGH);

    BlinkerMIOT.powerState("off");
    BlinkerMIOT.print();

    wsState = false;
    colorW = 0;
    ws2812fx.setBrightness(colorW);
    // 反馈开关状态
    Button_on.print("off");
    WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
  }
  BlinkerMIOT.print();
}

void miotColor(int32_t color)  //调整颜色
{
  BLINKER_LOG("need set color: ", color);

  colorR = color >> 16 & 0xFF;
  colorG = color >> 8 & 0xFF;
  colorB = color & 0xFF;

  BLINKER_LOG("colorR: ", colorR, ", colorG: ", colorG, ", colorB: ", colorB);
  BlinkerMIOT.color(color);
  BlinkerMIOT.print();

  ws2812fx.setColor(colorR, colorG, colorB);
  WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
}
//小爱切换模式,对小爱说把灯调到XX模式,就会执行相应的操作
void miotMode(uint8_t mode)  //在下方根据上面注释修改自己喜欢的模式的编号
{
  BLINKER_LOG("need set mode: ", mode);

  if (mode == BLINKER_CMD_MIOT_DAY) {
    //日光模式
    Mode = 10;
  } else if (mode == BLINKER_CMD_MIOT_NIGHT) {
    //夜光模式
    Mode = 20;
  } else if (mode == BLINKER_CMD_MIOT_COLOR) {
    //彩光
    Mode = 43;
  } else if (mode == BLINKER_CMD_MIOT_WARMTH) {
    // 温馨
    Mode = 2;
  } else if (mode == BLINKER_CMD_MIOT_TV) {
    // 电视模式
    Mode = 11;
  } else if (mode == BLINKER_CMD_MIOT_READING) {
    // 阅读模式
    Mode = 0;
    Slider_ColorTemperature_callback(2700);
  } else if (mode == BLINKER_CMD_MIOT_COMPUTER) {
    // 电脑模式
    Mode = 12;
  }
  ws2812fx.setMode(Mode);
  wsMode = mode;
  BlinkerMIOT.mode(mode);
  BlinkerMIOT.print();

  modeName.print(ws2812fx.getModeName(Mode));
}

void miotBright(const String& bright) {
  BLINKER_LOG("need set brightness: ", bright);
  colorW = bright.toInt();
  BLINKER_LOG("now set brightness: ", colorW);

  BlinkerMIOT.brightness(colorW);
  BlinkerMIOT.print();

  ws2812fx.setBrightness(colorW);
}

void miotColoTemp(int32_t colorTemp) {
  BLINKER_LOG("need set colorTemperature: ", colorTemp);
  BlinkerMIOT.colorTemp(colorTemp);
  BlinkerMIOT.print();
  Slider_ColorTemperature_callback(colorTemp);
}

void miotQuery(int32_t queryCode) {
  BLINKER_LOG("MIOT Query codes: ", queryCode);

  switch (queryCode) {
    case BLINKER_CMD_QUERY_ALL_NUMBER:
      BLINKER_LOG("MIOT Query All");
      BlinkerMIOT.powerState(wsState ? "on" : "off");
      BlinkerMIOT.color(getColor());
      BlinkerMIOT.mode(Mode);
      BlinkerMIOT.colorTemp(rgbToKelvin( colorR, colorG, colorB));
      BlinkerMIOT.brightness(colorW);
      BlinkerMIOT.print();
      break;
    case BLINKER_CMD_QUERY_POWERSTATE_NUMBER:
      BLINKER_LOG("MIOT Query Power State");
      BlinkerMIOT.powerState(wsState ? "on" : "off");
      BlinkerMIOT.print();
      break;
    case BLINKER_CMD_QUERY_COLOR_NUMBER:
      BLINKER_LOG("MIOT Query Color");
      BlinkerMIOT.color(getColor());
      BlinkerMIOT.print();
      break;
    case BLINKER_CMD_QUERY_MODE_NUMBER:
      BLINKER_LOG("MIOT Query Mode");
      BlinkerMIOT.mode(Mode);
      BlinkerMIOT.print();
      break;
    case BLINKER_CMD_QUERY_COLORTEMP_NUMBER:
      BLINKER_LOG("MIOT Query ColorTemperature");
      BlinkerMIOT.colorTemp(rgbToKelvin( colorR, colorG, colorB));
      BlinkerMIOT.print();
      break;
    case BLINKER_CMD_QUERY_BRIGHTNESS_NUMBER:
      BLINKER_LOG("MIOT Query Brightness");
      BlinkerMIOT.brightness(colorW);
      BlinkerMIOT.print();
      break;
    default:
      BlinkerMIOT.powerState(wsState ? "on" : "off");
      BlinkerMIOT.color(getColor());
      BlinkerMIOT.mode(Mode);
      BlinkerMIOT.colorTemp(rgbToKelvin( colorR, colorG, colorB));
      BlinkerMIOT.brightness(colorW);
      BlinkerMIOT.print();
      break;
  }
}

void dataRead(const String& data) {
  String IPADDRESS = WiFi.localIP().toString();
  IPaddr.print(IPADDRESS);

  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");  //WIFI信号强度

  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");


  WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
  Slider_Mode.print(Mode);                       //滑动条
  Slider_Speed.print(Speed);
  modeName.print(ws2812fx.getModeName(Mode));  //文字
}
/*******************************************************************************
*@ Description    将给定的色温值(tmpKelvin)转换为RGB颜色值  
*@ Input          :<tmpKelvin>输入的色温值（[1000,40000]单位：K，开尔文）
*@ Output         :
*@ Return         :
*******************************************************************************/
int Color_temperature_conversion(unsigned int tmpKelvin, unsigned char* r, unsigned char* g, unsigned char* b) {

  static double tmpCalc = 0;

  if (tmpKelvin < 1000) {
    tmpKelvin = 1000;
  }
  if (tmpKelvin > 40000) {
    tmpKelvin = 40000;
  }
  tmpKelvin = tmpKelvin / 100;

  /*----#red----------------------------------------------------------------------------*/
  if (tmpKelvin <= 66) {
    *r = 255;
  } else {
    tmpCalc = tmpKelvin - 60;
    tmpCalc = 329.698727446 * (pow(tmpCalc, -0.1332047592));
    *r = tmpCalc;
    if (*r < 0) {
      *r = 0;
    }

    if (*r > 255) {
      *r = 255;
    }
  }

  /*----#green----------------------------------------------------------------------------*/
  if (tmpKelvin <= 66) {
    tmpCalc = tmpKelvin;
    tmpCalc = 99.4708025861 * log(tmpCalc) - 161.1195681661;
    *g = tmpCalc;

    if (*g < 0) {
      *g = 0;
    }
    if (*g > 255) {
      *g = 255;
    }
  } else {
    tmpCalc = tmpKelvin - 60;
    tmpCalc = 288.1221695283 * (pow(tmpCalc, -0.0755148492));
    *g = tmpCalc;

    if (*g < 0) {
      *g = 0;
    }
    if (*g > 255) {
      *g = 255;
    }
  }

  /*----#blue----------------------------------------------------------------------------*/
  if (tmpKelvin >= 66) {
    *b = 255;
  } else if (tmpKelvin <= 19) {
    *b = 0;
  } else {
    tmpCalc = tmpKelvin - 10;
    tmpCalc = 138.5177312231 * log(tmpCalc) - 305.0447927307;
    *b = tmpCalc;
    if (*b < 0) {
      *b = 0;
    }
    if (*b > 255) {
      *b = 255;
    }
  }
  return 0;
}

// 定义RGB到色温的转换函数
int rgbToKelvin(int red, int green, int blue) {
  double X = -0.14282 * red + 1.54924 * green + -0.95641 * blue;
  double Y = -0.32466 * red + 1.57837 * green + -0.73191 * blue;
  double Z = -0.68202 * red + 0.77073 * green + 0.56332 * blue;  // 转换到xyY颜色空间
  double x = X / (X + Y + Z);
  double y = Y / (X + Y + Z);  // 计算色温值
  double n = (x - 0.3320) / (0.1858 - y);
  int kelvin = (int)(437 * (n * n * n) + 3607 * (n * n) + 6833 * n + 5520);
  return kelvin;
}
void setup() {
  delay(100);
  Serial.begin(115200);
  BLINKER_DEBUG.stream(Serial);
  //Blinker.begin(auth, ssid, pswd);
  Blinker.begin(auth);

#ifdef readHist
  EEPROM.begin(4096);
  Serial.println("已开启历史模式功能，读取历史设置数据中...");
  if (EEPROM.read(2505) != 255) {
    colorR = EEPROM.read(2500);
    delay(10);
    colorG = EEPROM.read(2501);
    delay(10);
    colorB = EEPROM.read(2502);
    delay(10);
    colorW = EEPROM.read(2503);
    delay(10);
    Speed = EEPROM.read(2504);
    delay(10);
    Mode = EEPROM.read(2505);
    delay(10);
    for (int i = 0; i < 6; i++) {
      Serial.println(EEPROM.read(i + 2500));
    }
  }
  if (EEPROM.read(2505) == 255) {
    Serial.println("读取存储模式失败...");
  }
#endif


  ws2812fx.init();
  ws2812fx.setBrightness(colorW);
  ws2812fx.setSpeed(Speed * 100);
  ws2812fx.setColor(colorR, colorG, colorB);
  ws2812fx.setMode(Mode);
  ws2812fx.start();
  Serial.println("模式：");
  Serial.println(ws2812fx.getModeName(Mode));


  BlinkerMIOT.attachPowerState(miotPowerState);
  BlinkerMIOT.attachColor(miotColor);                //颜色回调函数
  BlinkerMIOT.attachMode(miotMode);                  //模式
  BlinkerMIOT.attachBrightness(miotBright);          //亮度
  BlinkerMIOT.attachColorTemperature(miotColoTemp);  //色温
  BlinkerMIOT.attachQuery(miotQuery);                //反馈该控制状态
  //点灯科技APP控件回调绑定
  Button_on.attach(on_callback);
  Button_Save.attach(save_callback);
  Button_Read.attach(read_callback);
  WS2812.attach(ws2812_callback);
  Slider_ColorTemperature.attach(Slider_ColorTemperature_callback);
  Slider_Mode.attach(Mode_callback);
  Slider_Speed.attach(Speed_callback);
  Blinker.attachData(dataRead);

  modeSaved.print(Mode);                         //储存模式
  WS2812.print(colorR, colorG, colorB, colorW);  //上传RGB亮度信息
  WS2812.brightness(colorW);
  Slider_Mode.print(Mode);  //滑动条
  Slider_Speed.print(Speed);
  modeName.print(ws2812fx.getModeName(Mode));  //文字


  String IPADDRESS = WiFi.localIP().toString();
  Serial.println(IPADDRESS);
  IPaddr.print(IPADDRESS);
  Text2.print("RSSI:" + String(WiFi.RSSI()) + "dBm");            //WIFI信号强度
  unsigned long runtime = millis();                              // 获取当前运行时间（单位为毫秒）
  uint16_t days = runtime / (24 * 60 * 60 * 1000);               // 计算运行天数
  uint16_t minutes = (runtime % (24 * 60 * 60 * 1000)) / 60000;  // 计算剩余的分钟数
  Text3.print("运行时长：" + String(days) + "天 " + String(minutes) + "分钟");


  //长按按钮触发设备重置功能
  button.attachLongPressStop(deviceReset);
  if (WiFi.waitForConnectResult() == WL_CONNECTED) 
  {
    MDNS.begin(host);
    server.on("/", HTTP_GET, []() 
    {
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", serverIndex);
    });
    server.on(
      "/update", HTTP_POST, []() 
      {
        server.sendHeader("Connection", "close");
        server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
        ESP.restart();
      },
      []() 
      {
        HTTPUpload& upload = server.upload();
        if (upload.status == UPLOAD_FILE_START) 
        {
          Serial.setDebugOutput(true);
          WiFiUDP::stopAll();
          Serial.printf("Update: %s\n", upload.filename.c_str());
          uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
          if (!Update.begin(maxSketchSpace)) {  // start with max available size
            Update.printError(Serial);
          }
        } 
        else if (upload.status == UPLOAD_FILE_WRITE) 
        {
          if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) 
          {
            Update.printError(Serial);
          }
        } 
        else if (upload.status == UPLOAD_FILE_END) 
        {
          if (Update.end(true)) 
          {  // true to set the size to the current progress
            Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
          } 
          else 
          {
            Update.printError(Serial);
          }
          Serial.setDebugOutput(false);
        }
        yield();
      });
    server.begin();
    MDNS.addService("http", "tcp", 80);
 
    Serial.printf("Ready! Open http://%s.local in your browser\n", host);
  } 
  else 
  {
    Serial.println("WiFi Failed");
  }
}

void loop() {
  button.tick();
  Blinker.run();
  ws2812fx.service();

  server.handleClient();
  MDNS.update();
}

//72种模式
/*
#define FX_MODE_STATIC                   0
#define FX_MODE_BLINK                    1
#define FX_MODE_BREATH                   2
#define FX_MODE_COLOR_WIPE               3
#define FX_MODE_COLOR_WIPE_INV           4 
#define FX_MODE_COLOR_WIPE_REV           5
#define FX_MODE_COLOR_WIPE_REV_INV       6
#define FX_MODE_COLOR_WIPE_RANDOM        7
#define FX_MODE_RANDOM_COLOR             8
#define FX_MODE_SINGLE_DYNAMIC           9
#define FX_MODE_MULTI_DYNAMIC           10
#define FX_MODE_RAINBOW                 11
#define FX_MODE_RAINBOW_CYCLE           12
#define FX_MODE_SCAN                    13
#define FX_MODE_DUAL_SCAN               14
#define FX_MODE_FADE                    15
#define FX_MODE_THEATER_CHASE           16
#define FX_MODE_THEATER_CHASE_RAINBOW   17
#define FX_MODE_RUNNING_LIGHTS          18
#define FX_MODE_TWINKLE                 19
#define FX_MODE_TWINKLE_RANDOM          20
#define FX_MODE_TWINKLE_FADE            21
#define FX_MODE_TWINKLE_FADE_RANDOM     22
#define FX_MODE_SPARKLE                 23
#define FX_MODE_FLASH_SPARKLE           24
#define FX_MODE_HYPER_SPARKLE           25
#define FX_MODE_STROBE                  26
#define FX_MODE_STROBE_RAINBOW          27
#define FX_MODE_MULTI_STROBE            28
#define FX_MODE_BLINK_RAINBOW           29
#define FX_MODE_CHASE_WHITE             30
#define FX_MODE_CHASE_COLOR             31
#define FX_MODE_CHASE_RANDOM            32
#define FX_MODE_CHASE_RAINBOW           33
#define FX_MODE_CHASE_FLASH             34
#define FX_MODE_CHASE_FLASH_RANDOM      35
#define FX_MODE_CHASE_RAINBOW_WHITE     36
#define FX_MODE_CHASE_BLACKOUT          37
#define FX_MODE_CHASE_BLACKOUT_RAINBOW  38
#define FX_MODE_COLOR_SWEEP_RANDOM      39
#define FX_MODE_RUNNING_COLOR           40
#define FX_MODE_RUNNING_RED_BLUE        41
#define FX_MODE_RUNNING_RANDOM          42
#define FX_MODE_LARSON_SCANNER          43
#define FX_MODE_COMET                   44
#define FX_MODE_FIREWORKS               45
#define FX_MODE_FIREWORKS_RANDOM        46
#define FX_MODE_MERRY_CHRISTMAS         47
#define FX_MODE_FIRE_FLICKER            48
#define FX_MODE_FIRE_FLICKER_SOFT       49
#define FX_MODE_FIRE_FLICKER_INTENSE    50
#define FX_MODE_CIRCUS_COMBUSTUS        51
#define FX_MODE_HALLOWEEN               52
#define FX_MODE_BICOLOR_CHASE           53
#define FX_MODE_TRICOLOR_CHASE          54
#define FX_MODE_TWINKLEFOX              55
#define FX_MODE_RAIN                    56
#define FX_MODE_BLOCK_DISSOLVE          57
#define FX_MODE_ICU                     58
#define FX_MODE_DUAL_LARSON             59
#define FX_MODE_RUNNING_RANDOM2         60
#define FX_MODE_FILLER_UP               61
#define FX_MODE_RAINBOW_LARSON          62
#define FX_MODE_RAINBOW_FIREWORKS       63
#define FX_MODE_TRIFADE                 64
#define FX_MODE_VU_METER                65
#define FX_MODE_HEARTBEAT               66
#define FX_MODE_BITS                    67
#define FX_MODE_MULTI_COMET             68
#define FX_MODE_FLIPBOOK                69
#define FX_MODE_POPCORN                 70
#define FX_MODE_OSCILLATOR              71
*/
